package com.hawk.eye.server.service.impl;

import com.hawk.eye.api.storage.dto.LogMessageDTO;
import com.hawk.eye.api.webhook.IWebhookRequester;
import com.hawk.eye.common.encipher.MessageEncipher;
import com.hawk.eye.framework.cache.ICache;
import com.hawk.eye.framework.core.domain.entity.SysUser;
import com.hawk.eye.framework.exception.HawkEyeAuthException;
import com.hawk.eye.common.model.*;
import com.hawk.eye.client.client.RequestParam;
import com.hawk.eye.common.utils.*;
import com.hawk.eye.framework.exception.HawkEyeServerException;
import com.hawk.eye.framework.utils.*;
import com.hawk.eye.framework.plugins.webhook.WebhookFactory;
import com.hawk.eye.server.auth.ServiceAuthHolder;
import com.hawk.eye.server.auth.ServiceAuthMetadata;
import com.hawk.eye.server.constant.AlarmConstant;
import com.hawk.eye.server.convert.OuterWebhookConvert;
import com.hawk.eye.server.convert.ServiceConfigConvert;
import com.hawk.eye.server.domain.dto.resp.*;
import com.hawk.eye.server.domain.po.ServiceConfig;
import com.hawk.eye.server.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.*;

/**
 * @className: ServiceAlarmServiceImpl
 * @description:
 * @author: salad
 * @date: 2023/10/1
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class ServiceAlarmServiceImpl implements IServiceAlarmService {

    private final IServiceConfigService serviceConfigService;

    private final ITemplateService templateService;

    private final IOuterWebhookService webhookService;

    private final ISysUserService sysUserService;

    private final IWhiteListService whiteListService;

    private final IAlarmBaseLogService baseLogService;

    private final IRepairWorkOrderService repairWorkOrderService;

    private final MessageEncipher messageEncipher;

    private final ICache cache;

    private final ThreadPoolTaskExecutor taskExecutor;

    private final TransactionTemplate transactionTemplate;

    private final WebhookFactory webhookFactory;


    @Override
    public ServiceAuthData pushMetadata(ServiceMetadata serviceMetadata) {
        String serviceName = serviceMetadata.getServiceName();
        if (messageEncipher.enabled()) {
            serviceMetadata.setServiceName(messageEncipher.decrypt(serviceMetadata.getServiceName(), String.class));
            serviceMetadata.setToken(messageEncipher.decrypt(serviceMetadata.getToken(), String.class));
        }
        ServiceConfig sc = serviceConfigService.getServiceConfig(serviceMetadata);
        if (sc == null) {
            return null;
        }
        ServiceAuthData sad = new ServiceAuthData();
        sad.setServiceName(serviceName);
        String token = sc.getToken();
        if (!StringUtil.equal(token, serviceMetadata.getToken())) {
            throw ExceptionUtils.error("令牌错误,服务名:[" + serviceMetadata.getServiceName() + "]");
        }
        String uuid = StringUtil.randomUUID();
        ServiceAuthMetadata sam = new ServiceAuthMetadata(sc.getId(), sc.getServiceZnName(), sc.getServiceCnName(), uuid);
        cache.set(uuid, sam);
        if (messageEncipher.enabled()) {
            uuid = messageEncipher.encrypt(uuid);
        }
        sad.setAuthToken(uuid);
        return sad;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void pushAlarmLog(RequestParam requestParam) {
        ServiceAuthData sad = requestParam.getServiceAuthData();
        if (ObjectUtil.isAnyEmpty(sad, requestParam.getAlarmLogAsMap())) {
            return;
        }
        taskExecutor.execute(() -> {
            transactionTemplate.<Void>execute(status -> {
                try {
                    String token = sad.getAuthToken();
                    if (messageEncipher.enabled()) {
                        token = messageEncipher.decrypt(sad.getAuthToken(), String.class);
                    }
                    ServiceAuthMetadata sam  = cache.<ServiceAuthMetadata>get(token);
                    if (sam == null){
                        return null;
                    }
                    Map<String, Object> alarmLogAsMap = requestParam.getAlarmLogAsMap();
                    //查询服务信息
                    ServiceConfig serviceConfig = serviceConfigService.getServiceConfig(sam.getServiceZnName());
                    //服务配置未找到
                    ExceptionAssert.isEmptyThrow(serviceConfig,
                            () -> new HawkEyeServerException("服务名:[" + sam.getServiceZnName() + "]服务配置未找到"));
                    //是否禁用
                    if (serviceConfig.getDisenabled() == 1) {
                        return null;
                    }
                    //获取告警标识
                    LogMessageDTO logMessageDTO = baseLogService.resolveLogMessage(serviceConfig, alarmLogAsMap);
                    //是否是白名单
                    if (whiteListService.isWhiteList(serviceConfig.getId(), alarmLogAsMap, logMessageDTO.getAlarmTime())) {
                        return null;
                    }
                    //获取模板
                    TemplateRespDTO template = templateService.getTemplate(serviceConfig.getTemplateId());
                    ExceptionUtils.isEmptyThrow(template, "模板[{}]不存在", serviceConfig.getTemplateId());
                    ExceptionUtils.isEmptyThrow(template.getTemplateNodes(), "模板[{}]不存在", serviceConfig.getTemplateId());
                    //是否满足升级规则
                    alarmLogAsMap.put(AlarmConstant.ALARM_LEVEL, baseLogService.getUpgradesAlarm(alarmLogAsMap, serviceConfig, logMessageDTO));
                    baseLogService.saveLogMessage(logMessageDTO, template, alarmLogAsMap);
                    repairWorkOrderService.createRepairWorkOrderIfMatched(logMessageDTO);
                    //是否满足降噪规则
                    if (baseLogService.silenceAlarm(serviceConfig, logMessageDTO)) {
                        return null;
                    }
                    //构建和解析模板
                    String content = templateService.resolveTemplate(template, alarmLogAsMap);
                    //查询服务的webhook
                    OuterWebhookRespDTO webhook = webhookService.selectWebhook(serviceConfig.getId());
                    IWebhookRequester webhookRequester = webhookFactory.get(webhook.getType());
                    ExceptionUtils.isEmptyThrow(webhookRequester, "找不到[{}]对应的webhook请求类", webhook.getType());
                    //构建ServiceAlarmMessage
                    if (StringUtil.isNotBlank(content)) {
                        ServiceAlarmMessage serviceAlarmMessage = buildServiceAlarmMessage(serviceConfig, template, content, webhook);
                        webhookRequester.send(serviceAlarmMessage);
                    }
                    return null;
                } catch (Throwable e) {
                    status.setRollbackOnly();
                    throw e;
                }
            });
        });
    }

    public ServiceAlarmMessage buildServiceAlarmMessage(ServiceConfig serviceConfig,
                                                        TemplateRespDTO template, String content, OuterWebhookRespDTO webhook) {
        ServiceAlarmMessage sam = new ServiceAlarmMessage();
        sam.setContent(content);
        sam.setWebhookConfigMessage(OuterWebhookConvert.INSTANCE.to(webhook));
        ServiceConfigMessage serviceConfigMessage = ServiceConfigConvert.INSTANCE.to2(serviceConfig);
        SysUser sysUser = sysUserService.selectUserById(serviceConfig.getChargeUserId());
        ChargeUser chargeUser = new ChargeUser(sysUser.getNickName(), sysUser.getPhonenumber());
        serviceConfigMessage.setChargeUser(chargeUser);
        sam.setServiceConfigMessage(serviceConfigMessage);
        TemplateConfigMessage tcm = new TemplateConfigMessage();
        tcm.setId(template.getId());
        tcm.setTemplateNodes(template.getTemplateNodes());
        tcm.setTemplateName(template.getTemplateName());
        sam.setTemplateConfigMessage(tcm);
        return sam;
    }

}
